/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.flexengine.component;

import com.flexengine.engine.Finalizable;
import com.flexengine.engine.Initializable;
import com.flexengine.engine.Priority;
import com.flexengine.engine.Renderable;
import com.flexengine.engine.Updatable;
import com.flexengine.server.database.EntityModel;
import com.flexengine.logger.Logger;
import com.flexengine.math.Point2D;
import com.flexengine.math.Vector2D;
import com.flexengine.scene.SceneBundle;
import com.flexengine.view.GameWindow;
import com.flexengine.view.Graphics;
import com.flexengine.xml.Attribute;
import com.flexengine.xml.Root;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Insets;
import javax.persistence.Column;
import javax.persistence.MappedSuperclass;
import javax.persistence.Transient;

/**
 * @author Luann R. Athayde
 * @version 1.0
 * @since 1.0
 */
@Root
@MappedSuperclass
public class GameComponent extends EntityModel implements Updatable, Renderable, Initializable, Finalizable {

    public static final Color BASIC_BORDER_COLOR = new Color(160, 160, 180);
    
    @Attribute
    @Column
    protected String name;

    @Transient
    protected transient float x;

    @Transient
    protected transient float y;

    @Transient
    protected Dimension size;

    @Transient
    protected transient boolean visible;
    @Transient
    protected transient Priority priority;
    @Transient
    protected transient float hAlign;
    @Transient
    protected transient float vAlign;

    public GameComponent(String name, Priority p, float x, float y, Dimension size) {
        this.name = name;
        this.priority = p;
        this.x = x;
        this.y = y;
        this.size = size;
        this.visible = true;
        Insets insets = GameWindow.getInstance().getInsets();
        if (insets != null) {
            this.x += insets.left;
            this.y += insets.top;
        }
        hAlign = vAlign = -1;
    }

    public GameComponent(String name, float x, float y, Dimension size) {
        this(name, Priority.NORMAL_PRIORITY, x, y, size);
    }

    public GameComponent(String name, float x, float y) {
        this(name, Priority.NORMAL_PRIORITY, x, y, new Dimension());
    }

    public GameComponent(String name) {
        this(name, 0, 0);
    }

    public GameComponent() {
        this("Widget");
    }

    @Override
    public void init(SceneBundle bundle) {
        // --
    }

    @Override
    public void update(Long time, Long totalTime) {
        // -- do nothing...
    }

    @Override
    public void render(Graphics g) {
        // -- do nothing...
        //g.setColor(Color.white);
        //g.drawRect(x-1, y-1, size.width+2, size.height+2);
    }

    @Override
    public void end(SceneBundle bundle) {
        // --
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public Priority getPriority() {
        return priority;
    }

    public void setPriority(Priority priority) {
        this.priority = priority;
    }

    public Dimension getSize() {
        return size;
    }

    public int getWidth() {
        return size.width;
    }

    public int getHeight() {
        return size.height;
    }

    public Dimension getWinSize() {
        return GameWindow.getInstance().getSize();
    }

    public void setSize(Dimension size) {
        if (size != null) {
            this.size.setSize(size.width, size.height);
        } else {
            this.size = new Dimension(0, 0);
        }
    }

    public void setSize(double width, double height) {
        if (width >= 0.1 && width <= 1.0 && height >= 0.1 && height <= 1.0) {
            size.setSize(GameWindow.getInstance().getWidth() * width, GameWindow.getInstance().getHeight() * height);
        } else {
            this.size.setSize(width, height);
        }
    }

    @Override
    public boolean isVisible() {
        return visible;
    }

    public void setVisible(boolean visible) {
        this.visible = visible;
    }

    public float getX() {
        return x;
    }

    public void setX(float x) {
        this.x = x;
    }

    public float getY() {
        return y;
    }

    public void setY(float y) {
        this.y = y;
    }

    public void addX(float x) {
        this.x += x;
    }

    public void addY(float y) {
        this.y += y;
    }

    public static final int SCREEN_HORIZONTAL_LEFT_ALIGN = -10;
    public static final int SCREEN_HORIZONTAL_CENTER_ALIGN = -20;
    public static final int SCREEN_HORIZONTAL_RIGHT_ALIGN = -30;
    public static final int SCREEN_VERTICAL_TOP_ALIGN = -40;
    public static final int SCREEN_VERTICAL_CENTER_ALIGN = -50;
    public static final int SCREEN_VERTICAL_BOTTOM_ALIGN = -60;

    public synchronized GameComponent setPosition(float hAlign, float vAlign) {
        this.hAlign = hAlign;
        this.vAlign = vAlign;
        Dimension winSize = GameWindow.getInstance().getSize();
        if (hAlign == SCREEN_HORIZONTAL_LEFT_ALIGN) {
            x = 0;
        } else if (hAlign == SCREEN_HORIZONTAL_CENTER_ALIGN) {
            x = winSize.width / 2 - size.width / 2;
        } else if (hAlign == SCREEN_HORIZONTAL_RIGHT_ALIGN) {
            x = winSize.width - size.width;
        } else {
            x = hAlign;
        }
        if (vAlign == SCREEN_VERTICAL_TOP_ALIGN) {
            y = 0;
        } else if (vAlign == SCREEN_VERTICAL_CENTER_ALIGN) {
            y = winSize.height / 2 - size.height / 2;
        } else if (vAlign == SCREEN_VERTICAL_BOTTOM_ALIGN) {
            y = winSize.height - size.height;
        } else {
            y = vAlign;
        }
        return this;
    }

    public synchronized GameComponent setPosition(float hAling, float vAlign, float moveX, float moveY) {
        setPosition(hAling, vAlign);
        x += moveX;
        y += moveY;
        return this;
    }

    public synchronized GameComponent setBounds(float x, float y, float width, float height) {
        this.setPosition(x, y);
        this.setSize(width, height);
        return this;
    }

    public synchronized boolean colidsWithTopWindowBorder() {
        return colidsWithTopWindowBorder(null);
    }

    public synchronized boolean colidsWithTopWindowBorder(Vector2D acc) {
        int tmpY = 0;
        if (GameWindow.getInstance().getInsets() != null) {
            tmpY += GameWindow.getInstance().getInsets().top;
        }
        if (y <= tmpY) {
            //collide with left wall
            y = tmpY;
            if (acc != null) {
                acc.invertY();
            }
            return true;
        }
        return false;
    }

    public synchronized boolean colidsWithDownWindowBorder() {
        return colidsWithDownWindowBorder(null);
    }

    public synchronized boolean colidsWithDownWindowBorder(Vector2D acc) {
        int tmpY = GameWindow.getInstance().getHeight() - size.height;
        if (GameWindow.getInstance().getInsets() != null) {
            tmpY -= GameWindow.getInstance().getInsets().bottom;
        }
        if (y >= tmpY) {
            //collide with left wall
            y = tmpY;
            if (acc != null) {
                acc.invertY();
            }
            return true;
        }
        return false;
    }

    public synchronized boolean colidsWithLeftWindowBorder() {
        return colidsWithLeftWindowBorder(null);
    }

    public synchronized boolean colidsWithLeftWindowBorder(Vector2D acc) {
        int tmpX = 0;
        if (GameWindow.getInstance().getInsets() != null) {
            tmpX += GameWindow.getInstance().getInsets().left;
        }
        if (x <= tmpX) {
            //collide with left wall
            x = tmpX;
            if (acc != null) {
                acc.invertX();
            }
            return true;
        }
        return false;
    }

    public synchronized boolean colidsWithRightWindowBorder() {
        return colidsWithRightWindowBorder(null);
    }

    public synchronized boolean colidsWithRightWindowBorder(Vector2D acc) {
        int tmpX = GameWindow.getInstance().getWidth() - size.width;
        if (GameWindow.getInstance().getInsets() != null) {
            tmpX -= GameWindow.getInstance().getInsets().right;
        }
        if (x >= tmpX) {
            //collide with left wall
            x = tmpX;
            if (acc != null) {
                acc.invertX();
            }
            return true;
        }
        return false;
    }

    public synchronized boolean colidsWithWindowBorder() {
        return colidsWithDownWindowBorder() | colidsWithLeftWindowBorder()
                | colidsWithRightWindowBorder() | colidsWithTopWindowBorder();
    }

    public synchronized boolean colidsWithWindowBorder(Vector2D acc) {
        return colidsWithDownWindowBorder(acc) | colidsWithLeftWindowBorder(acc)
                | colidsWithRightWindowBorder(acc) | colidsWithTopWindowBorder(acc);
    }

    public float getHorizontalAlign() {
        return hAlign;
    }

    public float getVerticalAlign() {
        return vAlign;
    }

    public static final int COLISION_NONE = 0;
    public static final int COLISION_LEFT = 1;
    public static final int COLISION_RIGHT = 2;
    public static final int COLISION_TOP = 3;
    public static final int COLISION_DOWN = 4;

    /**
     *
     * @param x
     * @param y
     * @param width
     * @param height
     * @return
     */
    public synchronized int colidsWith(Point2D pos, Dimension size, Vector2D direct) {
        try {
            if (colidsWith(pos, size)) {
                if (x >= pos.getX() && y <= pos.getY() + size.height) {
                    if (direct != null) {
                        direct.invertY();
                    }
                    return COLISION_TOP;
                } else if (x >= pos.getX() && y + this.size.getHeight() >= pos.getY()) {
                    if (direct != null) {
                        direct.invertY();
                    }
                    return COLISION_DOWN;
                }
                if (x + this.size.getWidth() >= pos.getX() && y >= pos.getY() && y <= pos.getY() + size.getHeight()) {
                    if (direct != null) {
                        direct.invertX();
                    }
                    return COLISION_RIGHT;
                } else if (x <= pos.getX() + size.getWidth() && y >= pos.getY() && y <= pos.getY() + size.getHeight()) {
                    if (direct != null) {
                        direct.invertX();
                    }
                    return COLISION_LEFT;
                }
            }
        } catch (Exception e) {
            Logger.logError(getClass().getSimpleName() + ": " + e.getMessage());
        }
        return COLISION_NONE;
    }

    public synchronized int colidsWith(GameComponent another) {
        return colidsWith(
                new Point2D(another.x, another.y),
                another.size,
                null);
    }

    public synchronized int colidsWith(GameComponent another, Vector2D direct) {
        return colidsWith(
                new Point2D(another.x, another.y),
                another.size,
                direct);
    }

    /**
     * Verify the colision of THIS(A) with another object(B) (pos ^ size)...
     *
     * @param pos - Position of another object...
     * @param size - Size of another object...
     * @return true if colids, false otherwise...
     */
    public synchronized boolean colidsWith(Point2D pos, Dimension size) {
        return GameComponent.colidsWith(new Point2D(x, y), this.size, pos, size);
    }

    public static boolean colidsWith(Point2D aPos, Dimension aSize,
            Point2D bPos, Dimension bSize) {
        //- A à esquerda de B...
        if ((aPos.getX() + aSize.getWidth()) < bPos.getX()) {
            return false;
        }
        //- A à direita de B...
        if (aPos.getX() > (bPos.getX() + bSize.getWidth())) {
            return false;
        }
        //- A acima de B...
        if ((aPos.getY() + aSize.getHeight()) < bPos.getY()) {
            return false;
        }
        return aPos.getY() <= (bPos.getY() + bSize.getHeight());
    }

    // -------------------------------------------------------------------------
    @Override
    public String toString() {
        int w = -1, h = -1;
        if (size != null) {
            w = size.width;
            h = size.height;
        }
        return name + "{[x=" + x + ",y=" + y + "][w=" + size.width + ",h=" + size.height + "][" + visible + "]}";
    }

}
